package com.atguigu.srb.core.service.impl;

import com.alibaba.excel.EasyExcel;
import com.atguigu.srb.core.listener.ExcelDictDTOListener;
import com.atguigu.srb.core.mapper.DictMapper;
import com.atguigu.srb.core.pojo.dto.ExcelDictDTO;
import com.atguigu.srb.core.pojo.entity.Dict;
import com.atguigu.srb.core.service.DictService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 数据字典 服务实现类
 * </p>
 *
 * @author Bing
 * @since 2021-04-07
 */
@Slf4j
@Service
public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements DictService {

    @Resource
    private RedisTemplate redisTemplate;

    /**
     * Alibaba EasyExcel 数据批量导入
     *
     * @param inputStream
     */
    @Transactional(rollbackFor = Exception.class) // 开启事务
    @Override
    public void importData(InputStream inputStream) {
        // 读取文件类型 inputStream ；实体类 ExcelDictDTO ；监听器名字 ExcelDictDTOListener ；
        // 从 inputStream 读取数据，通过监听器，封装到实体类 ExcelDictDTO 对象里面
        // 把 baseMapper 传给 监听器
        EasyExcel.read(inputStream, ExcelDictDTO.class, new ExcelDictDTOListener(baseMapper)).sheet().doRead();
        log.info("Excel导入成功");
    }

    /**
     * Alibaba EasyExcel-导出-下载-Excel文件
     *
     * @return
     */
    @Override
    public List<ExcelDictDTO> listDictData() {
        // 根据 entity 条件，查询全部记录
        List<Dict> dictList = baseMapper.selectList(null);

        // 创建 ExcelDictDTO 列表，将 Dict 列表转换成 ExcelDictDTO 列表
        ArrayList<ExcelDictDTO> ExcelDictDTOList = new ArrayList<>(dictList.size());

        dictList.forEach(dict -> {
            // 对象拷贝给谁就 New 谁
            ExcelDictDTO excelDictDTO = new ExcelDictDTO();
            BeanUtils.copyProperties(dict, excelDictDTO); // 拷贝对象
            ExcelDictDTOList.add(excelDictDTO);
        });
        return ExcelDictDTOList;
    }

    /**
     * 根据上级id获取数据字典子节点数据列表
     *
     * @param parentId
     * @return
     */
    @Override
    public List<Dict> listByParentId(Long parentId) {
        try {
            // 首先查询 redis 中是否存入数据列表
            List<Dict> dictList = (List<Dict>) redisTemplate.opsForValue().get("srb:core:dictList:" + parentId);
            if (dictList != null) {
                // 如果存在 则从 redis 中直接返回数据列表
                log.info("从 redis 中获取数据列表");
                return dictList;
            }
        } catch (Exception e) {
            // 如果 redis 出现异常，不要抛出，默默打印日志，继续往下执行
            log.error("redis 服务器异常:" + ExceptionUtils.getStackTrace(e));
        }
        // 如果不存在 则查询数据库
        log.info("从数据库中获取数据列表数据");
        LambdaQueryWrapper<Dict> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Dict::getParentId, parentId);
        List<Dict> dictList = baseMapper.selectList(wrapper);
        // 填充 hasChildren 字段
        dictList.forEach(dict -> {
            // 判断当前节点是否有子节点，找到当前 dict 下级有没有子节点
            // 前端需要这个名字 hasChildren
            boolean hasChildren = this.hasChildren(dict.getId());
            dict.setHasChildren(hasChildren);
        });
        try {
            // 将数据存入 redis ,有效期 5 分钟
            log.info("将数据列表存入 redis");
            redisTemplate.opsForValue().set("srb:core:dictList:" + parentId, dictList, 5, TimeUnit.MINUTES);
        } catch (Exception e) {
            log.error("redis 服务器异常:" + ExceptionUtils.getStackTrace(e));
        }
        // 返回数据列表
        return dictList;
    }

    /**
     * 判断当前 id 所在的节点下是否有子节点
     *
     * @param id
     * @return
     */
    private boolean hasChildren(Long id) {
        LambdaQueryWrapper<Dict> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Dict::getParentId, id);
        Integer count = baseMapper.selectCount(wrapper);
        // intValue() 把 Integer 类型转化为 Int 类型
        if (count.intValue() > 0) {
            return true;
        }
        return false;
    }


}
